use super::*;

pub(crate) struct Args {
	pub name: Option<Path>,
	pub owner: Option<Path>,
	#[allow(unused)]
	pub ty: Option<Path>,
}
impl parse::Parse for Args {
	fn parse(input: parse::ParseStream) -> Result<Self> {
		let mut name = None;
		let mut owner = None;
		let mut ty = None;
		let metas = input.parse_terminated(syn::Meta::parse, Token![,])?;
		for meta in metas {
			match meta {
				Meta::NameValue(v) => match (&v.path, &v.value) {
					(n, Expr::Path(v)) if n.is_ident("name") => name = Some(v.path.clone()),
					(n, Expr::Path(v)) if n.is_ident("owner") => owner = Some(v.path.clone()),
					(n, Expr::Path(v)) if n.is_ident("ty") => ty = Some(v.path.clone()),
					_ => {}
				},
				_ => {}
			}
		}
		Ok(Args { name, owner, ty })
	}
}

pub(crate) fn expand(attr: &Args, input: &mut Item) -> TokenStream {
	match input {
		Item::Const(v) => expand_const(attr, v),
		Item::Fn(v) => expand_func(attr, v),
		Item::Static(v) => expand_static(attr, v),
		_ => quote! {#input},
	}
}
fn expand_const(attr: &Args, input: &mut ItemConst) -> TokenStream {
	let ty = &input.ident;
	let ts_name = match attr.name.try_ts().unwrap_or_else(|| input.ts()).lit_cstr() {
		Ok(v) => v,
		Err(e) => return e,
	};
	let export = match &attr.owner {
		Some(owner) => quote! {::napih::EXPORT_CONST!(field,#owner,#ts_name, #ty);},
		None => quote! {::napih::EXPORT_CONST!(#ts_name, #ty);},
	};
	let expr = &input.expr;
	#[cfg(feature = "dts")]
	let dts = build_dts(dts_const(attr, input));
	#[cfg(not(feature = "dts"))]
	let dts = TokenStream::new();
	input.expr = parse_quote! {{#export #dts #expr}};
	quote! {#input}
}
#[cfg(feature = "dts")]
fn dts_const(attr: &Args, input: &ItemConst) -> DTSResult {
	let name = attr.name.try_ts().unwrap_or_else(|| input.ts());
	let ret = input.ty.try_ts().ok_or("const ty")?.ts();
	let declare = if attr.owner.is_some() {
		"static readonly"
	} else {
		"export declare const"
	};
	Ok(fomat!((declare)" "(name)": "(ret)";").owner(name, TY_CONST, &attr.owner))
}
fn expand_func(attr: &Args, input: &mut ItemFn) -> TokenStream {
	let ty = &input.sig.ident;
	let ts_name = match attr.name.try_ts().unwrap_or_else(|| input.ts()).lit_cstr() {
		Ok(v) => v,
		Err(e) => return e,
	};
	let export = match &attr.owner {
		Some(owner) => quote! {::napih::EXPORT_CONST!(field_fn,#owner,#ts_name, #ty);},
		None => quote! {::napih::EXPORT_CONST!(func,#ts_name, #ty);},
	};
	let block = &input.block;
	#[cfg(feature = "dts")]
	let dts = build_dts(dts_func(attr, input));
	#[cfg(not(feature = "dts"))]
	let dts = TokenStream::new();
	input.block = parse_quote! {{#export #dts #block}};
	quote! {#input}
}
#[cfg(feature = "dts")]
fn dts_func(attr: &Args, input: &ItemFn) -> DTSResult {
	let name = attr.name.try_ts().unwrap_or_else(|| input.ts());
	let ret = attr.ty.try_ts().ok_or("need type on const func")?.ts();
	let declare = if attr.owner.is_some() {
		"static readonly"
	} else {
		"export declare const"
	};
	Ok(fomat!((declare)" "(name)": "(ret)";").owner(name, TY_CONST, &attr.owner))
}
fn expand_static(attr: &Args, input: &mut ItemStatic) -> TokenStream {
	let ty = &input.ident;
	let ts_name = match attr.name.try_ts().unwrap_or_else(|| input.ts()).lit_cstr() {
		Ok(v) => v,
		Err(e) => return e,
	};
	let export = match &attr.owner {
		Some(owner) => quote! {::napih::EXPORT_CONST!(field,#owner,#ts_name, #ty);},
		None => quote! {::napih::EXPORT_CONST!(#ts_name, #ty);},
	};
	let expr = &input.expr;
	#[cfg(feature = "dts")]
	let dts = build_dts(dts_static(attr, input));
	#[cfg(not(feature = "dts"))]
	let dts = TokenStream::new();
	input.expr = parse_quote! {{#export #dts #expr}};
	quote! {#input}
}
#[cfg(feature = "dts")]
fn dts_static(attr: &Args, input: &ItemStatic) -> DTSResult {
	let name = attr.name.try_ts().unwrap_or_else(|| input.ts());
	let ret = input.ty.try_ts().ok_or("const ty")?.ts();
	let declare = if attr.owner.is_some() {
		"static readonly"
	} else {
		"export declare const"
	};
	Ok(fomat!((declare)" "(name)": "(ret)";").owner(name, TY_CONST, &attr.owner))
}
